const msgHelp=require('../database/msgs/msgHelp')
const GroupHelp=require('../database/groups/GroupHelp')
const userHelp=require('../database/users/userHelp')
const UnReadMsgHelp=require('../database/unread/UnReadMsgHelp')
const commonUtils=require('../utils/commonUtils')
const JpushUtils=require('../utils/JpushUtils')


//在内存中保存一份socket表，以uid:socket的形式保存
var SocketObj = {};
global.SocketObj=SocketObj

//内存中保存的一份socket表，以uid:socket.id的形式保存
var socket_client={}


//内存中保存的一份clinetId表，以uid:clinetId的形式保存,注意用户触发踢下线动作
var cline_ids={}


//当前在线的socket,该参数好像没什么用了，但是也不影响功能，
var OnlineSockets={}




//服务端与客户端建立连接，服务端监听以下几种事件
export function Reply(socket,io) {


    //接受心跳包
    onGetBeat(socket,io)


    //通过connect来saveSocket
    onAutoConnect(socket,io)


    //保存socket表
    onSaveSocket(socket,io)


    //移除socket
    onRemoveSocket(socket,io)


    //单聊
    singleChat(socket,io)


    //群聊
    groupChat(socket,io)


    //读红点
    onReadMsg(socket,io)


    //读红点（群消息）
    onReadGroupMsg(socket,io)




    //断开连接,注意，该事件不需要自定义触发器，系统会自动调用
    socket.on('disconnect',(reason)=>{

    })


    socket.on("close", function (code, reason) {
        console.log("关闭连接")
    });
    
    socket.on("error", function (code, reason) {
        console.log("异常关闭")
        console.log(code)
        console.log(reason)
    });

}



//接受心跳包
function onGetBeat(socket) {
    socket.on('beat',async(req)=>{

        socket.emit('Test','beatbeat'+JSON.stringify(req))

    })
}



//客户端socket.io自动连接，不通过saveSocket
function  onAutoConnect(socket){

    //客户端创建时，带过来用户参数，
    let params=socket.request._query

    //uid不存在，不做后续处理
    if(params.uid==0){
        return
    }


    //connet带参数过来uid,
    if(params.uid){

        let req={
            uid:params.uid-0,
            clientId:params.clientId
        }

        //服务端保存客户端带过来的信息
        doSaveSocket(socket,req,'connect事件带过来')

    }
    else{

        //安卓的参数是通过handshake.query传过来的，与ios不同，原因大概是rn与ios建立连接的机制不同
        let params_android= socket.handshake.query;
        let req={
            uid:params_android.uid-0,
            clientId:params_android.clientId
        }

        //服务端保存客户端带过来的信息
        doSaveSocket(socket,req,'handshake事件带过来')

    }

}



//客户端主动发送saveSocket事件建立连接
function onSaveSocket(socket) {
    socket.on('saveSocket',async(req)=>{

        doSaveSocket(socket,req,req.reason)

    })
}



//保存socket
const doSaveSocket=async (socket,req,reason)=> {

    //为了兼容新旧版本，将用户表的appcVersion字段更新，如果用户存在，则更新
    let isExist=await userHelp.isUserExist(req.uid-0)
    if(isExist){
        let appVersion=req.appVersion? req.appVersion:''
        userHelp.updateUserAppVersion(req.uid-0,appVersion)
    }


    //用户的socket连接记录不存在，则保存socket
    if(!SocketObj[req.uid]){

        //保存最新的socket
        updateSocketObjs(socket,req.uid,req.clientId)

        //当用户连接上服务端，马上查看是否有遗留消息需要推送，如果有遗留信息，在客户端连上之后，立刻发送
        await  sendUnReadMsg(req.uid)

    }
    else{

        //如果socket已经存在，有两情况
        //1、同一个客户端先后连接了两次
        //2、用户在另一个客户端登录，对于这种情况，需要触发踢下线动作

        //判断是否触发踢下线
       await  kickOffLine(req,socket)


        //同样保存最新的socket
        updateSocketObjs(socket,req.uid,req.clientId)

        //发送遗留消息，上一步有可能触发踢下线动作，这里做了一个延时
        setTimeout(async()=>{
            await  sendUnReadMsg(req.uid)
        },1000)
    }
}



//保存最新的socket
function updateSocketObjs(socket,uid,clientId) {
    //保存socket,clientid,socketid
    SocketObj[uid] = socket
    socket_client[uid]=socket.id
    cline_ids[uid]=clientId


    //好像没什么用了，当时测试的时候有用的
    let key=socket.id
    OnlineSockets[key]={is_active:true,uid:uid}

}



//判断是否需要触发踢下线动作，存在小bug(不能反复提来踢去，不过这种情况实际使用中，概率较低)
function kickOffLine(req,socket){
    //旧的socket
    let old_socket=SocketObj[req.uid]

    //旧的客户端id
    let old_clientId=cline_ids[req.uid]

    //新的客户端id
    let clientId=req.clientId

    //如果两个id不同，表示相同用户在不同客户端登录了，触发踢下线动作
    if(old_clientId!=clientId){
        //旧用户被提下线
        old_socket.emit('offLine','你被踢下线,你的账号在其他手机上登录')
    }
}



//读取单聊消息，（用于消红点的）
function onReadMsg(socket) {
    socket.on('readMsg',async(req)=>{

        //对方uid
        let uid=req.uid

        //自己uid
        let myuid=req.myuid

        //读取消息，逻辑是：有一张未读消息的表，每个人都有一条记录，
        //该记录格式如下 {uid:[mid,mid]}后者{gid:[mid,mid]}，
        //在读消息时，将记录中uid的数组值为空，并把未读消息总数减去此数组长度,就表示把uid这个人的消息读了
        await msgHelp.readMsg(uid,myuid)


        //将当前某个用户的未读数量推送回去(对应消息列表的未读数)
        pushItemUnReadNum(socket,uid,myuid)

        //将总未读数量推送回去
        sendUnReadMsgNum(socket,myuid)

    })
}



//当前某个人的未读数(对应消息列表某个人的未读数)
const pushItemUnReadNum=async(socket,uid,myuid)=>{

    let unReadNum=await UnReadMsgHelp.getUnReadMsgNum(myuid-0,uid-0)

    let obj={
        id:uid+""+myuid,
        unReadNum:unReadNum,
    }

    socket.emit('itemUnReadNum',obj)

    return ''

}


//当前某个群的未读数(对应消息列表某个群的未读数)
const pushItemUnReadNumForGroup=async(socket,gid,myuid)=>{

    let unReadNum=await UnReadMsgHelp.getUnReadMsgNum(myuid-0,gid-0)

    let obj={
        id:gid+""+myuid,
        unReadNum:unReadNum,
    }

    socket.emit('itemUnReadNum',obj)

    return ''

}



//未读消息总数量
const sendUnReadMsgNum=async(socket,to_uid)=>{

    let num=await UnReadMsgHelp.getUnReadMsgNumTotal(to_uid-0)

    socket.emit('unReadMsgNum',num)

}


//读取群聊消息，（用于消红点的）
function onReadGroupMsg(socket) {
    socket.on('readGroupMsg',async(req)=>{

        //群id
        let gid=req.gid

        //自己的id
        let myuid=req.myuid


        //读取群消息，逻辑同读取单聊消息，
        await msgHelp.readGroupMsg(gid,myuid)


        //将当前某个群的未读数量推送回去(对应消息列表的未读数)
        pushItemUnReadNumForGroup(socket,gid,myuid)

        //总未读数量
        sendUnReadMsgNum(socket,myuid)
    })
}




//监听移除socket事件
function onRemoveSocket(socket) {
    socket.on('removeSocket',async(req)=>{
        let myuid=req.uid-0
        removeSocket(socket,'通过removeSocket事件移除',myuid)
    })

}


//处理移除socket事件
function removeSocket(socket,reason,myuid) {

    if(myuid){
        SocketObj[myuid]=null
    }

}


//发送未读消息，当用户连接上服务器后，判断该用户是否存在未读消息记录，有旧发送
const sendUnReadMsg=async(uid)=> {
    uid=uid-0
    //获取总的未读消息数组
    let msgs=await UnReadMsgHelp.getMsgsByMid(uid)

    if(msgs.length>0){

        //聊天室的遗留消息
        for(let item of msgs){
            //存在消息中存在gid，表明是群聊
            if(item.gid){

                if(SocketObj[uid]){

                    //新需求，版本兼容，要保证新版本用户发给老版本用户的msgType=4的消息，服务端转换为msgType=1。
                    let item_=await commonUtils.transformMsgType(item,uid)

                    SocketObj[uid].emit('groupChatReply',[item_])
                }
            }
            else{
                //单聊或者系统消息，系统消息也是走的单聊事件，只是type的字段不同，type=1单聊，2系统消息，3群聊
                if(SocketObj[uid]){

                    //新需求，版本兼容，要保证新版本用户发给老版本用户的msgType=4的消息，服务端转换为msgType=1。
                    let item_=await commonUtils.transformMsgType(item,uid)
                    SocketObj[uid].emit('singleChatReply',[item_])
                }
            }
        }


        //除了发送遗留消息，
        if(SocketObj[uid]){

            //还要发送客户端消息列表每个人（群）的最后一条消息(对应客户端消息列表，)
            pushLastMsgItem(SocketObj[uid],uid)

            //未读消息总数
            sendUnReadMsgNum(SocketObj[uid],uid)

        }
    }
}



//发送客户端消息列表每个人（群）的最后一条消息(对应客户端消息列表，)
const  pushLastMsgItem=async(socket,myuid)=> {

    //哪些人给了我发了消息，但是我没有读取
     let users= await  UnReadMsgHelp.getUsers(myuid-0)

    socket.emit('Test','调试：'+JSON.stringify(users))

    if(users.length>0){
        for(let uid of users){
            //群聊
            if(uid.length>7){
                let gid=uid
                //取出该群聊消息的最后一条
                let msgs=await msgHelp.getGroupMsgList(gid)
                let lastmsg=msgs[msgs.length-1]
                if(lastmsg){
                    //推送最后一条消息
                    pushLastMsgForGroup(SocketObj[myuid],myuid,gid,lastmsg)
                }
            }
            else{  //单聊

                //取出最后一条消息
                let msgs=await msgHelp.getMsgList(myuid,uid)
                let lastmsg=msgs[msgs.length-1]
                //推送
                pushLastMsg(SocketObj[myuid],myuid,uid,lastmsg)
            }
        }
    }
}



//判断App是否运行在前台
function AppIsOnLine(uid) {
    if(SocketObj[uid]){
        return true
    }
    return false
}



//接受单聊消息
function singleChat(socket,io) {
    socket.on('singleChat',async(req)=>{

        //发送消息的人是否登录过聊天服务器
        let is_fromUid_exist=await userHelp.isUserExist(req.from_uid)
        if(!is_fromUid_exist){
            socket.emit('singleChatReply','你未登录聊天服务器，请先登录');
            return
        }

        //接受消息的人是都登录过聊天服务器
        let is_exist=await userHelp.isUserExist(req.to_uid)
        if(!is_exist){
            socket.emit('singleChatReply','不存在该用户，该用户从未登录过聊天服务器');
            return
        }


        //单聊信息(存储消息时，加上发送人的头像地址，和用户名供客户端使用)
        if(!req.avatar||!req.display_name){
            let user_info=await userHelp.getUserInfo(req.from_uid)
            let avatar=user_info.pic
            let username=user_info.username
            req=Object.assign(req,{avatar:avatar,display_name:username})
        }

        //消息入库，并判断该消息是否存在（防止重复入库）
        let has_save_before=await msgHelp.saveMsg(req)
        //拿到此条消息（附加了一些头像等信息）
        let thismsg=await msgHelp.findMsgByClientTime(req.client_time)


        //之前有存储过此条消息，则该条消息为前端重复发送
        if(has_save_before){
            //那么只将消息推送给本人，不发送给对方
            socket.emit('singleChatReply',[thismsg])

            //并更新自己消息列表最后一条消息
            pushLastMsgForMe(socket,req.to_uid,req.from_uid,thismsg)
        }
        else{

            //首先保存一份未读消息；如果对方在线，接受到消息后，会立刻读掉消息；不在线的话，对方登录后会收到未读消息
            await saveUnReadMsg(req.to_uid-0,req.from_uid-0,thismsg.mid)


            //给自己返回消息
            socket.emit('singleChatReply',[thismsg])
            //更新自己消息列表最后一条消息
            pushLastMsgForMe(socket,req.to_uid,req.from_uid,thismsg)


            //给对方发送给消息
            //对方在线
            if(AppIsOnLine(req.to_uid)){

                let socket_id= socket_client[req.to_uid]
                //新需求，版本兼容，要保证新版本用户发给老版本用户的type=4的消息，服务端转换为type=1。
                let thismsg_=await commonUtils.transformMsgType(thismsg,req.to_uid)
                //给对方推送消息
                io.sockets.to(socket_id).emit('singleChatReply',[thismsg_])

                //给对方推送最后一条消息
                pushLastMsg(SocketObj[req.to_uid],req.to_uid,req.from_uid,thismsg)

                //给对方更新未读消息总数
                sendUnReadMsgNum(SocketObj[req.to_uid],req.to_uid)
            }
            else{
                //对方不在线，或者在后台，通过jpush提示他，收到一条新信息
                JpushSend(req.to_uid-0,req.user_agent,socket,1)
            }
        }
    })
}


//存储未读消息
const saveUnReadMsg=async (to_uid,from_uid,mid)=>{

    let msg=await UnReadMsgHelp.getUnReadMsg(to_uid)

    //写入对方的未读消息列表
    if(!msg){
        let  msg_ids={}
        //from_uid对于群聊而言，是gid
        msg_ids[from_uid]=[mid]
        let obj={
            to_uid:to_uid,
            msg_ids:msg_ids,
        }
       await UnReadMsgHelp.saveUnReadMsg(obj)
    }
    else{
        //更新
        let msg_ids=msg.msg_ids

        if(msg_ids[from_uid]){
            msg_ids[from_uid].push(mid)
        }
        else{
            msg_ids[from_uid]=[mid]
        }

        let unRead=msg.unRead-0

        let obj={
            to_uid:to_uid,
            msg_ids:msg_ids,
            unRead:unRead+1
        }
        await UnReadMsgHelp.updateUnReadMsg(obj,to_uid)
    }

    return ''

}





//极光推送
const JpushSend=async(uid,user_agent,socket,pushType,gid)=> {


    let extras={type:"huaren_chat",msg:'你收到一条新消息'}

    JpushUtils.push(uid+'',1,extras,user_agent,socket,pushType,gid)
}



//推送最后一条消息，给对方
const pushLastMsg=async(socket,to_uid,from_uid,lastmsg)=> {
    if(!lastmsg){
        return ''
    }
    let  lastmsg_=await msgHelp.pushLastMsg(to_uid-0,from_uid-0,lastmsg)

    if(lastmsg_){
        if(socket){
            //新需求，版本兼容，要保证新版本用户发给老版本用户的type=4的消息，服务端转换为type=1。
            let lastmsg_1=await commonUtils.transformMsgType(lastmsg_,to_uid)
            socket.emit('pushLastMsg',lastmsg_1)
        }

    }

    return ''
}


//推送最后一条消息，给自己
const pushLastMsgForMe=async(socket,to_uid,myuid,lastmsg)=> {
    if(!lastmsg){
        return ''
    }

    let  lastmsg_=await msgHelp.pushLastMsgForMe(to_uid-0,myuid-0,lastmsg)

    if(lastmsg_){
        if(socket){
            //新需求，版本兼容，要保证新版本用户发给老版本用户的type=4的消息，服务端转换为type=1。
            let lastmsg_1=await commonUtils.transformMsgType(lastmsg_,to_uid-0)
            socket.emit('pushLastMsg',lastmsg_1)
        }
    }

    return ''
}



//推送最后一条消息（群聊）
const pushLastMsgForGroup=async(socket,to_uid,gid,lastmsg)=> {
    if(!lastmsg){
        return ''
    }

    let  lastmsg_=await msgHelp.pushLastMsgForGroup(to_uid-0,gid-0,lastmsg)

    if(lastmsg_){
        if(socket){

            //新需求，版本兼容，要保证新版本用户发给老版本用户的type=4的消息，服务端转换为type=1。
            let lastmsg_1=await commonUtils.transformMsgType(lastmsg_,to_uid-0)
            socket.emit('pushLastMsg',lastmsg_1)
        }
    }

    return ''
}



//群聊
function groupChat(socket,io) {
    socket.on('groupChat',async(req)=>{

        //发送者是否登录聊天服务器
        let is_fromUid_exist=await userHelp.isUserExist(req.from_uid)
        if(!is_fromUid_exist){
            socket.emit('groupChatReply','你未登录聊天服务器，请先登录');
            return
        }

        //获取该群所有群成员
        let user_ids=await  GroupHelp.getGroupUserIds(req.gid)

        //给群消息补充发送者的头像和昵称等信息
        if(!req.avatar||!req.display_name){
            let user_info=await userHelp.getUserInfo(req.from_uid)
            let avatar=user_info.pic
            let username=user_info.username
            req=Object.assign(req,{avatar:avatar,display_name:username})
        }

        //消息入库，并返回该消息是否已经存在，防止重复入库
        let has_save_before=await msgHelp.saveMsg(req)
        //拿到该消息
        let thismsg=await msgHelp.findMsgByClientTime(req.client_time)

        //之前有存储过此条消息，则该条消息为前端重复发送
        if(has_save_before){
            //只需给发送者自己发送即可
            socket.emit('groupChatReply',[thismsg])
            if(thismsg){
                //给自己返回最后一条消息（群聊）
                pushLastMsgForGroup(socket,req.from_uid,req.gid,thismsg)
            }
        }
        else{
            //不管群成员在不在线，先存储一份未读消息；若在线，未读消息会立即被读取掉
            await saveUnReadMsgForGroup(user_ids,req.from_uid,req.gid,thismsg.mid)

            //对每一个群成员进行判断
            for(let userid of user_ids){

                //若群成员在线
                if(AppIsOnLine(userid)){

                    //新需求，版本兼容，要保证新版本用户发给老版本用户的msgType=4的消息，服务端转换为msgType=1。
                    let thismsg_=await commonUtils.transformMsgType(thismsg,userid)
                    //给群成员发送群聊消息
                    SocketObj[userid].emit('groupChatReply',[thismsg_])

                    //并把最后一条消息推送给群成员
                    if(thismsg){
                        pushLastMsgForGroup(SocketObj[userid],userid,req.gid,thismsg)
                    }

                    //并发送未读消息数
                    sendUnReadMsgNum(SocketObj[userid],userid)

                }
                else{

                    //不在线，发送jpush
                    if(userid!=req.from_uid){   //自己发消息时，自己肯定在线，不需要给自己推送

                        JpushSend(userid,req.user_agent,socket,3,req.gid)

                    }

                }
            }
        }
    })
}


//存储群聊未读消息
const saveUnReadMsgForGroup=async(user_ids,from_uid,gid,mid)=> {

    //即对每一个群成员，出了自己以外，都保留一份未读消息
    for(let userid of user_ids){
        if(userid!=from_uid){
            await  saveUnReadMsg(userid-0,gid-0,mid)
        }
    }
    return ''
}







